import fs from 'fs'
import crypto from 'crypto'
import { Certificate, PrivateKey } from '@fidm/x509'
import { isPlainObject } from './utils'
import { sm2 } from 'sm-crypto'

export function md5 (str, encoding = 'utf8') {
  return crypto
    .createHash('md5')
    .update(str, encoding)
    .digest('hex')
}

export function sha256 (str, key, encoding = 'utf8') {
  return crypto
    .createHmac('sha256', key)
    .update(str, encoding)
    .digest('hex')
}

export function getNonceStr (length = 16) {
  let str = ''
  while (str.length < length) {
    str += Math.random().toString(32).substring(2)
  }
  return str.substring(0, length)
}

export function loadCertFromPath (filePath) {
  return Certificate.fromPEM(fs.readFileSync(filePath))
}

export function loadCertFromContent (content) {
  if (typeof content === 'string') content = Buffer.from(content)
  return Certificate.fromPEM(content)
}

export function loadPrivateKeyFromPath (filePath) {
  return PrivateKey.fromPEM(fs.readFileSync(filePath))
}

export function loadPrivateKeyFromContent (content) {
  if (typeof content === 'string') content = Buffer.from(content)
  return PrivateKey.fromPEM(content)
}

export function decodeBase64 (str) {
  return Buffer.from(str, 'base64').toString('utf-8')
}

export function decryptCiphertext (ciphertext, key, nonce, associatedData) {
  const encryptData = Buffer.from(ciphertext, 'base64')
  const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce)

  decipher.setAuthTag(encryptData.slice(-16))
  decipher.setAAD(Buffer.from(associatedData))

  const result = Buffer.concat([
    decipher.update(encryptData.slice(0, -16)),
    decipher.final()
  ])

  return result.toString()
}

/**
 * 使用私钥创建 RSA-SHA256 签名
 * @param {String} privateKey 私钥
 * @param {String} str 签名字符串
 * @param {HexBase64Latin1Encoding} format=base64 签名格式
 * @returns {Buffer}
 */
export function signSha256WithRsaPSS (privateKey, str, format = 'base64') {
  return crypto.createSign('RSA-SHA256').update(Buffer.from(str), 'utf8').sign({
    key: privateKey,
    padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
    saltLength: 32
  }).toString(format)
}
/**
 * 使用 RSA 公钥加密数据
 * @param {String} data 数据
 * @param {String} publicKey 公钥
 */
export function rsaPublicKeyEncryptData (data, publicKey) {
  return crypto.publicEncrypt({
    key: publicKey,
    padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
  }, Buffer.from(data))
}
/**
 * 使用 RSA 私钥解密数据
 * @param {String|Buffer} encryptData 数据
 * @param {String} privateKey 私钥
 */
export function rsaPrivateKeyDecryptData (encryptData, privateKey) {
  if (typeof encryptData === 'string') encryptData = Buffer.from(encryptData)

  const decrypted = crypto.privateDecrypt({
    key: privateKey,
    padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
  }, encryptData)
  return decrypted.toString()
}

export function rsaPublicKeyVerifySign (publicKey, str, sign) {
  return crypto.createVerify('RSA-SHA256').update(str, 'utf8').verify(publicKey, sign, 'base64')
}

export function getQueryStr (obj, sort = false) {
  let queryArr = Object.keys(obj).filter(key => obj[key] !== null && obj[key] !== undefined)

  if (queryArr.length <= 0) return ''

  if (sort) {
    queryArr.sort()
  }

  queryArr = queryArr
    .map(key => {
      const val = isPlainObject(obj[key]) ? getQueryStr(obj[key], sort) : obj[key]

      if (val !== undefined && val !== null) {
        return key + '=' + val
      }
    }).filter(Boolean)

  return queryArr.join('&')
}

export function formatKey (key, type) {
  return `-----BEGIN ${type}-----\n${key}\n-----END ${type}-----`
}

/**
 * SM2算法验签
 * @param data
 * @param sign
 * @param publicKey
 * @returns {boolean|*}
 */
export function sm2VerifySign (data, sign, publicKey) {
  return sm2.doVerifySignature(data, Buffer.from(sign, 'base64').toString('hex'), publicKey, {
    hash: true,
    der: true
  })
}
